package com.conversationboard.controller;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.security.Principal;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;

import javax.mail.internet.AddressException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.joda.time.DateTime;

import com.conversationboard.cache.BannedUserCache;
import com.conversationboard.config.Configuration;
import com.conversationboard.controller.ipaddress.IPAddress;
import com.conversationboard.formatter.Formatter;
import com.conversationboard.formatter.IllegalMessageException;
import com.conversationboard.logger.Logger;
import com.conversationboard.model.Board;
import com.conversationboard.model.Boards;
import com.conversationboard.model.MessageFloodChecker;
import com.conversationboard.model.PollItem;
import com.conversationboard.model.Question;
import com.conversationboard.model.User;

public class NewQuestion {

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

		response.setCharacterEncoding("UTF-8");
		request.setCharacterEncoding("UTF-8");

		String sessionKey = request.getParameter("sKey");

		Principal principal = request.getUserPrincipal();
		String yourName = "";
		User user = null;

		if (principal != null) {
			yourName = principal.getName();
		} else {
			/* Board must support unregistered posting */
			user = User.getAnonymousUser();

			String displayName = request.getParameter("displayname");
			displayName = Formatter.escapeHtml(displayName);

			if ((displayName == null) || displayName.trim().length() == 0) {
				request.setAttribute("message", "Please set your display name.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			if (displayName.length() > 80) {
				displayName = displayName.substring(0, 79);
			}

			/* Add (Unregistered) on to user name */

			String unregisteredTag = " (unregistered)";

			String fullName = displayName + unregisteredTag;

			if (fullName.length() > 80) {
				fullName = displayName.substring(0, (80 - unregisteredTag.length()));
				fullName = fullName + unregisteredTag;
			}

			user.setDisplayName(fullName);
			user.setLoginId(IPAddress.getIPAddress(request));
		}

		String ipAddress = IPAddress.getIPAddress(request);
		String questionText = (String) request.getParameter("questionText").trim();
		String questionTitle = (String) request.getParameter("questionTitle").trim();
		int boardId = Integer.parseInt(request.getParameter("boardId"));
		String poll = request.getParameter("poll");
		boolean isPoll = false;
		String adminId = request.getParameter("adminId");

		if ((adminId != null) && (adminId.trim().equals(""))) {
			adminId = null;
		}

		if (poll.equalsIgnoreCase("true")) {
			isPoll = true;
		}

		if (!sessionKey.equals(request.getSession().getId())) {
			return;
		}

		/* Truncate titles/messages that are too long */

		if (questionText.length() > Configuration.getInstance().getMaxMessageSize()) {
			questionText = questionText.substring(0, Configuration.getInstance().getMaxMessageSize());
		}

		if (questionTitle.length() > 200) {
			questionTitle = questionTitle.substring(0, 200);
		}

		/* Don't let people post no title or text */

		if ((questionTitle.length() == 0) || (questionText.length() == 0)) {
			request.setAttribute("message", "Please insert a question title and question text.");
			RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
			dispatcher.forward(request, response);
			return;
		}

		try {

			if (user == null) {
				user = User.get(yourName);
			}

			user.setIpAddress(ipAddress);

			MessageFloodChecker.check(user);

			/*
			 * For anonymous admins, change the display name, by copying the
			 * user and making temporary substitutions
			 */

			if (user.isBoardAdmin(boardId) && (adminId != null) && (!adminId.equals(user.getDisplayName()))) {
				user = User.copyUser(user);
				user.setDisplayName(adminId);
				String tempAddress = "127.0.0.1";
				user.setIpAddress(tempAddress);
				int number = adminId.hashCode();
				String loginId = adminId.replace(" ", "") + number + "@hotmail.com";
				user.setLoginId(loginId + "@hotmail.com");

				/*
				 * Knobble the creation date to pass "isAbleToPost" test. Admins
				 * are always allowed to.
				 */

				DateTime dateTime = new DateTime(1, 1, 1, 1, 1, 1, 1);
				user.setCreationDate(dateTime.toDate());
			}

			/* Don't let the user post a question if they're banned */

			if (BannedUserCache.isBanned(boardId, false, yourName, ipAddress)) {
				request.setAttribute("message", "You have been barred.");
				RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
				dispatcher.forward(request, response);
				return;
			}

			Board board = Boards.getBoard(boardId);

			/* Are you authorised to post threads on this board? */

			if (!board.isPostable(user)) {
				RequestDispatcher dispatcher = request.getRequestDispatcher("/index.jsp");
				dispatcher.forward(request, response);
				return;
			}

			Question question = new Question(questionTitle, questionText, boardId, user);

			/*
			 * If it's a poll, and polls are enabled, add the poll items to the
			 * question
			 */

			if (isPoll && board.isAllowPolls()) {

				String pollItems = request.getParameter("pollItems").trim();
				String closingDateText = request.getParameter("closingDate").trim();

				Date closingDate = processDateString(closingDateText);

				if (closingDate == null || (!closingDate.after(new Date()))) {
					request.setAttribute("message", "Please enter a date in the format YYYY-MM-DD, e.g. 2000-12-31, for 31st December 2000. Please make sure the data is greater than today.");
					RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
					dispatcher.forward(request, response);
					return;
				}

				List<PollItem> options = this.processPollItems(user, board, pollItems);

				/* Ensure the user has entered more than one poll item */

				if (options.size() <= 1) {
					request.setAttribute("message", "Please insert more than one poll item.");
					RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
					dispatcher.forward(request, response);
					return;
				}

				if (options.size() > Configuration.getInstance().getMaxPollItems()) {
					request.setAttribute("message", "The maximum number of poll items is: " + Configuration.getInstance().getMaxPollItems());
					RequestDispatcher dispatcher = request.getRequestDispatcher("/Pages/messagepage.jsp");
					dispatcher.forward(request, response);
					return;
				}

				question.setPollItems(options);
				question.setPollClosingDate(closingDate);
			}

			board.addQuestion(null, question);

			response.sendRedirect(request.getContextPath() + "/NewAnswersControllerServlet?boardid=" + boardId);

		} catch (IllegalMessageException e) {
			throw new ServletException(e);
		} catch (ParseException e) {
		} catch (SQLException e) {
			throw new ServletException(e);
		} catch (AddressException e) {
			throw new ServletException(e);
		} catch (Throwable t) {

			Date now = new Date();
			String header = "Throwable caught on NewQuestionControllerServlet. Board: " + boardId + " posted by user " + yourName + " at: " + now + "\n\n";

			StringWriter writer = new StringWriter();
			t.printStackTrace(new PrintWriter(writer));
			user = User.getOnlyIfInCache(yourName);
			Logger.log(now, header + writer.toString(), user);
			throw new ServletException(t);

		}

	}


	private List<PollItem> processPollItems(User user, Board board, String pollItems) throws IllegalMessageException {

		/* Escape any HTML formatting */

		pollItems = Formatter.escapeHtml(pollItems);

		StringTokenizer tokeniser = new StringTokenizer(pollItems, "\n");
		List<PollItem> options = new LinkedList<PollItem>();

		int optionId = 1;

		while (tokeniser.hasMoreTokens()) {

			PollItem item = new PollItem();

			String token = tokeniser.nextToken();

			token = token.trim();

			if (token.length() > 120) {
				token = token.substring(0, 119);
			}

			if (token.length() > 0) {
				item.setOptionText(token);
				item.setOptionId(optionId++);
				options.add(item);
			}
		}

		/* Process the BBCode */

		for (PollItem item : options) {
			item.setOptionText(Formatter.asHtml(board, user, item.getOptionText(), true));
		}

		return options;

	}


	private Date processDateString(String dateString) throws ParseException {

		if (!dateString.matches("^[0-9]{4}-[0-9]{2}-[0-9]{2}$")) {
			return null;
		}

		SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
		Date date = formatter.parse(dateString);

		return date;

	}

}
